package bll

import (
	"context"
	"encoding/json"
	"fmt"
	Redis "github.com/felixrobcoding/go-common/redis"
	"go.uber.org/zap"
	"maya-service/assistant"
	"maya-service/bll/weixin"
	log "maya-service/collector/logger"
	"maya-service/collector/trace_log"
	"maya-service/constant"
	"maya-service/event"
	"maya-service/model"
	modelEvent "maya-service/model/event"
	"maya-service/model/po"
	"maya-service/store/redis"
	"maya-service/utils"
	"time"
)

var OrderLogicInstance = NewOrderLogic()

type OrderLogic struct {
}

func NewOrderLogic() *OrderLogic {

	return &OrderLogic{}
}

func (o *OrderLogic) Init() {
	// 订单抢单成功
	event.Subscribe(event.GrabOrderSuccess, func(ctx context.Context, e event.Event, payload interface{}) {
		data, ok := payload.(*modelEvent.GrabOrderSuccess)
		if !ok {
			return
		}

		// 生效订单
		err := o.OrderExecute(ctx, data.OrderNo, data.GrabUser)
		if err != nil {
			log.Error("############ OrderLogic 生效订单失败", zap.Any("data", utils.ToJsonString(data)))
		}

		return
	})

	// 用户进入会话
	event.Subscribe(event.MsgReceive, func(ctx context.Context, e event.Event, payload interface{}) {
		data, ok := payload.(*modelEvent.EnterSession)
		if !ok {
			return
		}

		var err error
		if data.OpenKfId != "" {
			log.Info(" ---------------- 收到消息", zap.Any("data", utils.ToJsonString(data)), zap.Any("err", err))
			var message string
			message, _, err = weixin.ReadMessageFromKfLink(data.Token, data.OpenKfId)
			if err != nil {
				return
			}
			err = o.UserEnterSessionNtf(message, data)
			if err != nil {
				log.Info("###### MsgReceive UserEnterSessionNtf ", zap.Any("err", err))
			}

			if err != nil && err.Error() == "上分布式锁失败" {
				return
			}
			_ = o.UserAgreeNtf(message, data)
		}

		return
	})

	// 用户进入会话
	event.Subscribe(event.Test, func(ctx context.Context, e event.Event, payload interface{}) {
		data, ok := payload.(*modelEvent.DeliverySession)
		if !ok {
			return
		}
		unix := time.Now().Unix()
		if unix-data.CreateTime < 3600*1 {
			_ = assistant.AsynqQueue.GetAsynqClient().Publisher("test", &modelEvent.DeliverySession{
				OrderNo:    data.OrderNo,
				GrabUserId: data.GrabUserId,
			}, time.Second*30)
			return
		}
	})

	// 分配会话
	event.Subscribe(event.DeliverySession, func(ctx context.Context, e event.Event, payload interface{}) {
		data, ok := payload.(*modelEvent.DeliverySession)
		if !ok {
			return
		}

		log.Info(" ############ event.DeliverySession 开始重新分配会话 ", zap.Any("data", utils.ToJsonString(data)))

		trace_log.DebugLogger(data.OrderNo, "##########  重试队列中取出会话，重新开始分配会话 grabUserId：", data.GrabUserId, " data:", utils.ToJsonString(data))
		err := o.AllocSession(data.OrderNo, data.GrabUserId)
		if err != nil {
			unix := time.Now().Unix()
			if unix-data.CreateTime < 3600*1 {
				trace_log.DebugLogger(data.OrderNo, "##########  订单丢进重新分配队列 unix:", unix, " data.CreateTime", data.CreateTime, "grabUserId：", data.GrabUserId, " data:", utils.ToJsonString(data), " err:", err)
				log.Info(" ############### event.DeliverySession 订单丢进重新分配队列", zap.String("orderNo", data.OrderNo), zap.Any("grabUserId", data.GrabUserId), zap.Any("err", err))
				data.Count++
				_ = assistant.AsynqQueue.GetAsynqClient().Publisher("delivery_session", data, time.Second*10)
				return
			}
			trace_log.DebugLogger(data.OrderNo, " ############### event.DeliverySession 分配会话失败 订单丢弃，执行失败 orderNo", data.OrderNo, " grabUserId:", data.GrabUserId, "err", err)
			log.Info(" ############### event.DeliverySession 订单丢弃，执行失败", zap.String("orderNo", data.OrderNo), zap.Any("grabUserId", data.GrabUserId), zap.Any("err", err))
			return
		}

		// 返回链接给用户
		log.Info(" ############### event.DeliverySession 订单发货执行完成", zap.String("orderNo", data.OrderNo), zap.Any("grabUserId", data.GrabUserId), zap.Any("err", err))
		return
	})

	return
}

// OrderExecute 订单执行
func (o *OrderLogic) OrderExecute(ctx context.Context, orderNo, grabUserId string) (err error) {
	trace_log.DebugLogger(orderNo, "########## start 抢单成功开始执行发货，分配会话 grabUserId：", grabUserId)
	log.Info(" ############### OrderExecute 开始执行", zap.String("orderNo", orderNo), zap.Any("grabUserId", grabUserId))
	// 获取订单信息
	info, _ := Order.Find(ctx, &model.OrderInfoRequest{
		OrderNo: orderNo,
	})

	if info == nil || info.Id <= 0 {
		log.Error("######### 订单发货失败, 订单不存在OrderLogic", zap.Any("orderNo", orderNo), zap.Any("grabUserId", grabUserId))
		return
	}

	// 获取督导师相应的获客链接
	// 通过获客链接分配会话
	err = o.AllocSession(orderNo, grabUserId)
	if err != nil {
		trace_log.DebugLogger(orderNo, "########## OrderExecute 订单丢进重新分配队列 grabUserId：", grabUserId, " err:", err)
		log.Info(" ############### OrderExecute 订单丢进重新分配队列", zap.String("orderNo", orderNo), zap.Any("grabUserId", grabUserId), zap.Any("err", err))
		_ = assistant.AsynqQueue.GetAsynqClient().Publisher("delivery_session", &modelEvent.DeliverySession{
			OrderNo:    orderNo,
			GrabUserId: grabUserId,
			CreateTime: time.Now().Unix(),
			Count:      1,
		}, time.Second*30)
		return
	}

	trace_log.DebugLogger(orderNo, "########## end 抢单成功执行发货，分配会话完成 grabUserId：", grabUserId)
	// 返回链接给用户
	log.Info(" ############### OrderExecute 订单发货执行完成", zap.String("orderNo", orderNo), zap.Any("grabUserId", grabUserId), zap.Any("err", err))
	return
}

func (o *OrderLogic) GetServiceStatus(openKfId, userId string) int {
	externalUserId, err := redis.KV.HGet(context.Background(), "quanxi:user_externalId", userId).Result()
	if err != nil {
		return -1
	}

	if externalUserId == "" {
		return -1
	}

	status := weixin.GetServiceState(openKfId, externalUserId)
	return status
}

// UserEnterSessionNtf 用户进入会话
func (o *OrderLogic) UserEnterSessionNtf(message string, data *modelEvent.EnterSession) (err error) {
	_, orderNo, externalUserId := weixin.ReadUserClickMessage(message)
	if orderNo != "-1" {
		return fmt.Errorf("orderNo is not empty")
	}

	// 用户进入会话
	externalUserId, _, welComeCode, msgId := weixin.ReadExternUserIdFromMessage(message, data.OpenKfId)
	var unionId = ""
	if externalUserId != "" {
		// 获取unionId
		unionId = weixin.GetUserBaseInfo(externalUserId)
		if unionId != "" {
			redis.KV.HSet(context.Background(), "quanxi:user_externalId", unionId, externalUserId)
		}
	}

	if externalUserId == "" {
		err = fmt.Errorf("UserEnterSessionNtf externalUserId empty")
		return
	}

	// 获取客服状态
	status := weixin.GetServiceState(data.OpenKfId, externalUserId)
	if status == 4 || status == 3 {
		err = fmt.Errorf("UserEnterSessionNtf status == 4 || status == 3 ")
		return
	}

	// 获取unionId
	if unionId == "" {
		unionId = weixin.GetUserBaseInfo(externalUserId)
	}

	// 获取订单信息
	orderStatus := 7
	list, _ := Order.List(context.Background(), &model.OrderListRequest{
		CustomerId:  &unionId,
		OrderStatus: &orderStatus,
		Index:       1,
		Size:        1,
	})

	if list == nil {
		err = fmt.Errorf("UserEnterSessionNtf list == nil ")
		return
	}

	if len(list.List) <= 0 {
		log.Info(" ############### UserEnterSessionNtf 查询订单失败", zap.Any("unionId", unionId), zap.Any("externalUserId", externalUserId))
		err = fmt.Errorf("UserEnterSessionNtf 没有待生效的订单 ")
		return
	}

	// 上分布式锁
	lock := Redis.TRedLock{}
	bSuccess, err := lock.Lock(redis.KV, fmt.Sprintf("UserEnterSessionNtf_%v", externalUserId), time.Second*10)
	if err != nil || !bSuccess {
		return fmt.Errorf("上分布式锁失败")
	}
	defer lock.UnLock(redis.KV)

	// 特殊状态下直接进行生效
	if status == 2 {
		/*		kfId := GetKfIdByOrder(list.List[0].OrderId)
				trace_log.DebugLogger(list.List[0].OrderId, "########## UserEnterSessionNtf  status == 2 开始生效订单，推送消息 orderNo:", list.List[0].OrderId)
				o.effectiveOrderAndNotify(list.List[0].OrderId, externalUserId, kfId)*/
		level := constant.GetLevelByKfId(data.OpenKfId)
		userList := GetSuperVisorList([]int{level})
		if len(userList) > 0 {
			_ = weixin.AllockSession(list.List[0].OrderId, data.OpenKfId, externalUserId, userList[0], 3)
			_ = weixin.AllockSession(list.List[0].OrderId, data.OpenKfId, externalUserId, userList[0], 4)
			status = weixin.GetServiceState(data.OpenKfId, externalUserId)
		} else {
			trace_log.DebugLogger(list.List[0].OrderId, "##########  UserEnterSessionNtf status == 2 没有导师可以进行分配")
		}
		return
	}

	kfId := GetKfIdByOrder(list.List[0].OrderId)
	// 记录待抢订单信息
	data1 := &po.GrabOrderInfo{
		OrderInfo:      *list.List[0],
		ExternalUserId: externalUserId,
		CustomId:       unionId,
		KfId:           kfId,
	}
	redis.KV.Set(context.Background(), fmt.Sprintf("quanxi:wait_grab_order:%v", list.List[0].OrderId), utils.ToJsonString(data1), time.Minute*60*24)

	trace_log.DebugLogger(list.List[0].OrderId, "########## 用户进入会话成功，生成redis订单记录 welComeCode", welComeCode, " msgId:", msgId, " status:", status, " \nuserList：", utils.ToJsonString(data1))

	// 给用发送欢迎消息
	level := constant.GetLevelByKfId(data.OpenKfId)
	if welComeCode != "" {
		err := weixin.SendWelcomeMessage(welComeCode, list.List[0].OrderId)
		if err != nil {
			trace_log.DebugLogger(list.List[0].OrderId, "########## 用户进入会话成功 1 发送欢迎消息失败 status:", status, " level:", level, " err:", err)
			err := weixin.SendMessageWithMenu(externalUserId, data.OpenKfId, list.List[0].OrderId)
			if err != nil {
				trace_log.DebugLogger(list.List[0].OrderId, "########## 用户进入会话成功 2 发送欢迎消息失败  status:", status, " level:", level, "err:", err)
			} else {
				trace_log.DebugLogger(list.List[0].OrderId, "########## 用户进入会话成功 2 发送欢迎消息成功  status:", status, " level:", level)
			}
		} else {
			trace_log.DebugLogger(list.List[0].OrderId, "########## 用户进入会话成功 1 发送欢迎消息成功 status:", status, " level:", level)
		}
	} else {
		err := weixin.SendMessageWithMenu(externalUserId, data.OpenKfId, list.List[0].OrderId)
		if err != nil {
			trace_log.DebugLogger(list.List[0].OrderId, "########## 用户进入会话成功 2 发送欢迎消息失败  status:", status, " level:", level, "err:", err)
		} else {
			trace_log.DebugLogger(list.List[0].OrderId, "########## 用户进入会话成功 2 发送欢迎消息成功  status:", status, " level:", level)
		}
	}
	return
}

// UserAgreeNtf 用户点击同意
func (o *OrderLogic) UserAgreeNtf(message string, data *modelEvent.EnterSession) (err error) {
	isReject, orderNo, externalUserId := weixin.ReadUserClickMessage(message)
	defer func() {
		log.Info("###### UserAgreeNtf ", zap.Any("isReject", isReject), zap.Any("orderNo", orderNo), zap.Any("externalUserId", externalUserId), zap.Any(" data", utils.ToJsonString(data)), zap.Any("err", err))
		if orderNo != "-1" {
			trace_log.DebugLogger(orderNo, "########## UserAgreeNtf 用户同意服务，开始生效订单，推送消息 orderNo:", orderNo, " err:", err)
		}
	}()

	if orderNo == "-1" {
		return
	}

	if isReject {
		// 拒绝
		log.Info("############# UserAgreeNtf 用户拒绝", zap.Any("orderNo", orderNo), zap.Any("orderNo", orderNo))
		if orderNo != "" {
			trace_log.DebugLogger(orderNo, "########## 用户拒绝 orderNo:", orderNo)
		}
		err = fmt.Errorf("用户拒绝了服务，订单不生效")
		return
	}

	// 获取unionId
	unionId := weixin.GetUserBaseInfo(externalUserId)

	// 获取订单信息
	orderStatus := 7
	list, _ := Order.List(context.Background(), &model.OrderListRequest{
		CustomerId:  &unionId,
		OrderStatus: &orderStatus,
		Index:       1,
		Size:        1,
	})

	if list == nil {
		err = fmt.Errorf("订单列表没有待生效的订单")
		return
	}

	if len(list.List) <= 0 {
		log.Info(" ############### UserEnterSessionNtf 查询订单失败", zap.Any("unionId", unionId), zap.Any("externalUserId", externalUserId))
		err = fmt.Errorf("订单列表没有待生效的订单")
		return
	}

	if orderNo != list.List[0].OrderId {
		return
	}

	kfId := GetKfIdByOrder(list.List[0].OrderId)

	trace_log.DebugLogger(orderNo, "########## UserAgreeNtf 开始生效订单，推送消息 orderNo:", orderNo)
	o.effectiveOrderAndNotify(list.List[0].OrderId, externalUserId, kfId)
	return
}

// effectiveOrderAndNotify 生效订单并发送抢单通知
func (o *OrderLogic) effectiveOrderAndNotify(orderNo string, externalUserId string, kfId string) {
	// 准备订单、发送订单
	orderStatus := 0
	_ = Order.Update(context.Background(), &model.OrderUpdateRequest{
		OrderId:     &orderNo,
		OrderStatus: &orderStatus,
	})

	// 更新缓存
	event.Publish(context.Background(), event.RefreshOrderCache, nil)

	// 发送通知
	event.Publish(context.Background(), event.UserEnterNtf, &modelEvent.OrderCreateNotify{
		OrderId: orderNo,
	})
	log.Info(" ############### UserEnterSessionNtf redis中生成待抢记录", zap.String("orderNo", orderNo))

	// 发送欢迎语
	status := weixin.GetServiceState(kfId, externalUserId)
	weixin.BatchSendMsg(orderNo, externalUserId, kfId, 0, status)
}

// AllocSession 分配会话
func (o *OrderLogic) AllocSession(orderNo string, grabUserId string) (err error) {
	log.Info("############# AllocSession 开始分配会话", zap.Any("orderNo", orderNo), zap.Any("grabUserId", utils.ToJsonString(grabUserId)))
	result, err := redis.KV.Get(context.Background(), fmt.Sprintf("quanxi:wait_grab_order:%v", orderNo)).Result()
	if err != nil {
		return err
	}

	kfId := GetKfIdByOrder(orderNo)
	data := &po.GrabOrderInfo{}
	_ = json.Unmarshal([]byte(result), data)
	if data.ExternalUserId == "" || data.CustomId == "" {
		return fmt.Errorf("redis 中待抢记录信息为空")
	}

	trace_log.DebugLogger(orderNo, "##########  AllocSession 开始分配会话 ExternalUserId：", data.ExternalUserId, " grabUserId: ", grabUserId, " KfId:", kfId)
	// 执行分配会话
	err = weixin.AllockSession(orderNo, kfId, data.ExternalUserId, grabUserId, 3)
	if err != nil {
		msg := ""
		if err.Error() == "督导师不在线" {
			msg = "督导师不在线"
		}

		if err.Error() == "会话未建立" {
			msg = "会话未建立"
		}

		trace_log.DebugLogger(orderNo, "##########  AllocSession 分配会话失败 err:", err, " msg:", msg, " ExternalUserId：", data.ExternalUserId, " grabUserId: ", grabUserId, " KfId:", kfId)

		log.Error("############# AllocSession err", zap.Any("err", err), zap.Any("data", utils.ToJsonString(data)))
		return fmt.Errorf(msg)
	}

	remark := fmt.Sprintf("用户：%v 订单：%v 分配给 %v ", orderNo, data.CustomerId, grabUserId)
	_ = Order.Update(context.Background(), &model.OrderUpdateRequest{
		Id:      data.Id,
		Remarks: &remark,
	})

	// 删除订单记录
	redis.KV.Del(context.Background(), fmt.Sprintf("quanxi:wait_grab_order:%v", orderNo))

	// 发送抢单成功通知
	event.Publish(context.Background(), event.RefreshOrderCache, nil)

	trace_log.DebugLogger(orderNo, "##########  AllocSession 分配会话成功 err:", err, " ExternalUserId：", data.ExternalUserId, " grabUserId: ", grabUserId, " KfId:", kfId)

	log.Info("############# AllocSession 分配成功", zap.Any("orderNo", orderNo), zap.Any("data", utils.ToJsonString(data)))
	return
}
